#include <QHostInfo>
#include <QQmlContext>
#include <QQmlEngine>
#include "ROComponent.h"

#include "QQuickROChannel.h"
#include "ROSyncCreation.h"
#include "RONode.h"
#include "QQuickROData.h"
#include "ROSyncProperty.h"

class RONodePrivate
{
public:
    RONodePrivate(RONode* _Q):_Q(_Q){




    }

    void createObjects(const QVariantList creations){
        for(auto i:creations)
        {
            auto param = i.toMap();

            QStringList reason = param["reason"].toStringList();
            CreationInfo createIds = param["createIds"].value<CreationInfo>();
            int root = param["root"].toInt();
            QObject* obj = nullptr;
            bool isRoot = false;
            if(reason[0] == "root")//该对象是根组件 因为根组件才创建出来的
            {
                if(!_Q->m_rootComponent)
                {
                    qDebug()<<"无法创建根对象，根对象组为空";
                    continue;
                }

                obj = _Q->m_rootComponent->create(_Q->m_rootComponent->creationContext());
                obj->setParent(_Q);
                isRoot = true;
            }
            else if(reason[0] == "direct")//该对象是直接调用创建方法创建出来的
            {
                QString createMethod = reason[1];
                QQmlComponent* comp = nullptr;
                int parentId = reason[2].toInt();

                auto parentData = m_objs.value(parentId);
                if(!parentData)
                {
                    continue;
                }

                if(createMethod == "comp")//该对象是通过组件创建出来的
                {
                    int compId = reason[3].toInt();

                    auto data = m_objs.value(compId);
                    if(data)
                    {
                        comp = qobject_cast<QQmlComponent*>(data->obj);
                    }

                }
                else if(createMethod == "url")//该对象是通过url创建出来的
                {
                    QString url = reason[3];
                    comp = m_comps.value(url);
                    if(!comp)
                    {
                        auto engine = qmlContext(_Q)->engine();
                        comp = new QQmlComponent(engine,QUrl(url),_Q);
                        m_comps[url] = comp;
                    }
                }

                if(comp)
                {
                    obj = comp->create(comp->creationContext());
                    obj->setParent(parentData->obj);
                }

            }
            else if(reason[0] == "syncObj"){//该对象是同步对象创建出来的
                auto id = reason[1].toInt();
                auto o = m_objs.value(id);
                if(o)
                {
                    auto syncobj = qobject_cast<ROSyncCreation*>(o->obj);
                    if(syncobj)
                    {
                        obj = syncobj->handleCreation({reason.begin() + 2,reason.end()});
                    }
                }
            }

            if(obj)
            {
                registerObject(obj,root,createIds);
                auto syncObjs = obj->findChildren<ROSyncObject*>();
                auto rootSync = qobject_cast<ROSyncObject*>(obj);
                if(rootSync)
                {
                    syncObjs << rootSync;
                }

                for(auto i:syncObjs)
                {
                    initializeObject(i);
                }

                if(isRoot)
                {
                    _Q->setRootObject(obj);
                }
            }

        }
    }

    void onRemoteObjectDestroied(const QVariant& value){
        auto id = value.value<ObjectId>();

        auto data = m_objs.value(id);

        if(data)
        {
            delete data->obj;
        }
    }

    void updateObjectPropertys(const QVariantList& propertyUpdates){
        for(auto i : propertyUpdates)
        {
            auto param = i.toMap();
            auto id = param["id"].toInt();
            auto propsValues = param["props"].toMap();
            auto data = m_objs.value(id);
            if(data)
            {
                auto obj = qobject_cast<ROSyncProperty*>(data->obj);

                QVariant newValues = propsValues;
                convertFromNet(newValues);
                obj->handleProperties(newValues.toMap());
            }

        }
    }

    void convertForNet(QVariant& value){
        convertValueForNet(value,[=](QObject* obj){

            auto data = getROObjectData(obj);
        if(!data)
        {
            return NullId;
        }

        return data->id;
        });
    }

    void convertFromNet(QVariant& value){
        convertValueFromNet(value,[=](ObjectId id)->QObject*{
            if(m_objs.contains(id))
            {
                return m_objs[id]->obj;
            }

            return nullptr;
        });
    }

    void initializeObject(ROSyncObject* syncObj){
        if(syncObj)
        {
            syncObj->initialize();
        }
    }

    void registerObject(QObject* obj,int id,const CreationInfo& genIds){

        ObjectDataPtr data = std::make_shared<ObjectData>();

        data->node = _Q;
        data->id = id;
        data->obj = obj;

        m_objs[data->id] = data;

        QObject::connect(obj,&QObject::destroyed,_Q,[=](){
            unRegisterObject(obj);
        });

        setROObjectData(obj,data);

        auto children = obj->children();

        auto childrenIds = genIds.value(id);

        for(auto i = 0;i<std::min(children.size(),childrenIds.size());i++)
        {
            auto child = children[i];
            registerObject(child,childrenIds[i],genIds);
        }
    }

    void unRegisterObject(QObject* obj){
        auto data = getROObjectData(obj);

        if(data)
        {
            m_objs.remove(data->id);
        }

    }

    bool blockSetActive = false;

    QQuickROChannel* channel = nullptr;

    QHash<ObjectId,ObjectDataPtr> m_objs;

    QHash<QString,QQmlComponent*> m_comps;//存储由url生成的组件缓存

    RONode* _Q = nullptr;
};

RONode::RONode():_P(new RONodePrivate(this))
{

}

RONode::~RONode()
{
    cleanScene();
    delete _P;
}

QUrl RONode::host() const
{
    return m_host;
}

void RONode::setHost(const QUrl &newHost)
{
    if (m_host == newHost)
        return;
    m_host = newHost;
    emit hostChanged();
}

RONode::State RONode::state() const
{
    return m_state;
}

void RONode::setState(State newState)
{
    if (m_state == newState)
        return;
    m_state = newState;

    emit stateChanged();
}

void RONode::connectToHost()
{

    _P->channel = new QQuickROChannel(this);

    auto host = this->host();

    auto scheme = host.scheme();

    if(scheme != "qtro")
    {
        markError(ConnectionFailed,QStringLiteral("url协议不正确，必须是qtro"));
        return;
    }

    QString address = host.host();

    QHostAddress addr;

    if(!addr.setAddress(address))
    {
        //生成主机地址失败，可能是个域名
        auto addresses = QHostInfo::fromName(address).addresses();

        if(addresses.isEmpty())
        {
            markError(ConnectionFailed,QStringLiteral("无法解析域名:%1").arg(address));
            return;
        }
        addr = addresses[0];
    }

    int port = host.port(2425);

    _P->channel->connectTo(addr,port);

    connect(_P->channel,&QQuickROChannel::stateChanged,this,[=](){
        if(_P->channel->state() == QQuickROChannel::Ready)
        {
            onConnected();
        }
        else if(_P->channel->state() == QQuickROChannel::Disconnected)
        {
            onDisConnected();
        }
    });

    connect(_P->channel,&QQuickROChannel::newMessage,this,&RONode::onMessage);

    setState(Connecting);
}

void RONode::onConnected()
{
    setState(Initializing);
    _P->channel->write("roLogin",QVariantMap{{"loginParam",loginParam()}});
}

void RONode::onDisConnected()
{
    _P->channel->deleteLater();
    _P->channel = nullptr;

    cleanScene();

    _P->blockSetActive = true;
    setActive(false);
    _P->blockSetActive = false;
    if(error() == NoError)
    {
        markError(LostConnection,QStringLiteral("与服务器失去连接"));
    }

}

void RONode::onMessage(QString topic, QVariant value)
{
    if(topic == "roInit")
    {
        initScene(value);
        _P->channel->write("roReady",{});
        setState(Ready);
    }
    else if(topic == "roNewObject")
    {
        _P->createObjects(value.toList());
    }
    else if(topic == "roPropertyUpdate")
    {
        _P->updateObjectPropertys(value.toList());
    }
    else if(topic == "roRPCCall")
    {
        handleRPC(value.toMap());
    }
    else if(topic == "roRemoveObject")
    {
        _P->onRemoteObjectDestroied(value);
    }
    else if(topic == "roKicked")
    {
        markError(Kicked,value.toMap()["msg"].toString());
    }
    else if(topic == "roLoginRejected")
    {
        markError(ConnectionRejected,value.toString());
    }
    else if(topic == "roServerClose")
    {
        markError(ServerClosed,QStringLiteral("服务器已关闭"));
    }
}

void RONode::initScene(const QVariant &value)
{
    auto map = value.toMap();

    _P->createObjects(map["roNewObject"].toList());
    _P->updateObjectPropertys(map["roPropertyUpdate"].toList());
    setPlayerId(map["playerId"].toInt());
}

void RONode::disconnectFromHost()
{
    _P->channel->write("roLogout",{});
    markError(Logout,QStringLiteral("已登出"));
    _P->channel->disconnect();
}

void RONode::rpcCall(int id, QString name, const QVariantList &params,bool isSafe)
{
    _P->channel->write("roRPCCall",QVariantMap{{"param",params},{"name",name},{"id",id}},isSafe?QQuickROChannel::Safe:QQuickROChannel::Quick);
}

void RONode::handleRPC(const QVariantMap &v)
{
    int id = v["id"].toInt();
    QString name = v["name"].toString();
    QVariantList params = v["param"].toList();

    auto data = _P->m_objs.value(id);
    if(!data)
    {
        return;
    }

    invokeMethod(data->obj,name,params);
}

void RONode::cleanScene()
{
    while (!_P->m_objs.empty()) {
        delete (*_P->m_objs.begin())->obj;
    }
}

QObject *RONode::getObjectById(int id)
{
    auto data = _P->m_objs.value(id);
    if(data)
    {
        return data->obj;
    }

    return nullptr;
}

void RONode::markError(Error err, QString errStr)
{
    setErrString(errStr);
    setError(err);
}

void RONode::clearError()
{
    setErrString({});
    setError({});
}

RONode::Error RONode::error() const
{
    return m_error;
}

void RONode::setError(Error newError)
{
    if (m_error == newError)
        return;
    m_error = newError;
    emit errorChanged();
}

QObject *RONode::rootObject() const
{
    return m_rootObject;
}

void RONode::setRootObject(QObject *newRootObject)
{
    if (m_rootObject == newRootObject)
        return;
    m_rootObject = newRootObject;
    emit rootObjectChanged();
}

QQmlComponent *RONode::rootComponent() const
{
    return m_rootComponent;
}

void RONode::setRootComponent(QQmlComponent *newRootComponent)
{
    if (m_rootComponent == newRootComponent)
        return;
    m_rootComponent = newRootComponent;
    emit rootComponentChanged();
}

int RONode::playerId() const
{
    return m_playerId;
}

void RONode::setPlayerId(int newPlayerId)
{
    if (m_playerId == newPlayerId)
        return;
    m_playerId = newPlayerId;
    emit playerIdChanged();
}

QVariantMap RONode::loginParam() const
{
    return m_loginParam;
}

void RONode::setLoginParam(const QVariantMap &newLoginParam)
{
    if (m_loginParam == newLoginParam)
        return;
    m_loginParam = newLoginParam;
    emit loginParamChanged();
}

QString RONode::errString() const
{
    return m_errString;
}

void RONode::setErrString(const QString &newErrString)
{
    if (m_errString == newErrString)
        return;
    m_errString = newErrString;
    emit errStringChanged();
}

bool RONode::active() const
{
    return m_active;
}

void RONode::setActive(bool newActive)
{
    if (m_active == newActive)
        return;
    m_active = newActive;
    emit activeChanged();

    if(!_P->blockSetActive)
    {
        if(m_active)
        {
            connectToHost();
        }
        else{
            disconnectFromHost();
        }
    }


}
